defmodule LastTimerServer do
	use GenServer
	require Logger

	def start_link(_) do
		GenServer.start_link(__MODULE__, [], name: __MODULE__)
	end

	def info() do
		GenServer.call(__MODULE__, :info)
	end

	def clear() do
		GenServer.call(__MODULE__, :clear)
	end

	def cancel(key) do
		GenServer.cast(__MODULE__, {:cancel, key})
	end


	@doc """
	执行 mfa 的时候,参数 a = [key, id | a],用于删除执行过的定时器
	"""
	def new(key, second, m, f, a, cancel_old \\ true) when is_integer(second) do
		new(key, NaiveDateTime.add(TimeLib.now(), second), m, f, a, cancel_old)
	end
	def new(key, ts, m, f, a, cancel_old) do
		GenServer.cast(__MODULE__, {:new, key, ts, m, f, a, cancel_old})
	end
	def expire(key, id) do
		GenServer.cast(__MODULE__, {:expire, key, id})
	end

	def init(_opts) do
		{:ok, %{}, {:continue, :do_init}}
	end


	def handle_continue(:do_init, state) do
		{:noreply, state}
	end

	def handle_call(:info, _from, state) do
		{:reply, state, state}
	end
	def handle_call(:clear, _from, state) do
		Enum.each(
			state,
			fn ({key, {_, tref, _}}) ->
				tref != nil && :timer.cancel(tref)
			end
		)
		{:reply, :ok, %{}}
	end
	def handle_call(_msg, _from, state) do
		{:reply, :ok, state}
	end

	def handle_cast({:new, key, ts, m, f, a, cancel_old}, state) do
		{_, old_tref, old_ts} = Map.get(state, key, {nil, nil, TimeLib.default_max_date()})
		now = TimeLib.now()
		id = SnowIdLib.get_int!()
		state = if NaiveDateTime.compare(ts, old_ts) == :lt do
			old_tref != nil && cancel_old && :timer.cancel(old_tref)
			sec = NaiveDateTime.diff(ts, now, :millisecond)
			case sec > 0 && :timer.apply_after(sec, __MODULE__, :run, [key, id, m, f, a]) do
				{:ok, tref} ->
					Map.put(state, key, {id, tref, ts})
				{:error, reason} ->
					Logger.error("Start Timer Error, #{inspect(reason)}, #{inspect({key, ts, m, f, a})}")
					state
				false ->
					spawn(__MODULE__, :run, [key, id, m, f, a])
					state
			end
		else
			if NaiveDateTime.compare(now, old_ts) == :gt do
				sec = NaiveDateTime.diff(ts, now, :millisecond)
				case :timer.apply_after(sec, __MODULE__, :run, [key, id, m, f, a]) do
					{:ok, tref} ->
						Map.put(state, key, {id, tref, ts})
					{:error, reason} ->
						Logger.error("Start Timer Error, #{inspect(reason)}, #{inspect({key, ts, m, f, a})}")
						state
				end
			else
				state
			end
		end
		{:noreply, state}
	end
	def handle_cast({:cancel, key}, state) do
		{:noreply, Map.delete(state, key)}
	end
	def handle_cast({:expire, key, id}, state) do
		state = expire(state, key, id)
		{:noreply, state}
	end


	def run(key, id, m, f, a)   do
		try do
			expire(key, id)
			apply(m, f, a)
		rescue
			reason ->
				"Run Timer Error, reason: #{inspect(reason)}, stacktrace: #{inspect(__STACKTRACE__)} "
				<> "m:#{m},f:#{f},a:#{inspect(a)}"
				|> Logger.error()
				LastTimerServer.new(key, 60, m, f, a)
		catch
			error ->
				Logger.error("Run Timer Error,reason: #{inspect(error)},m:#{m},f:#{f},a:#{inspect(a)}")
				LastTimerServer.new(key, 60, m, f, a)
		end
	end

	defp expire(state, key, id) do
		{old_id, _, _} = Map.get(state, key, {nil, nil, nil})
		state = if old_id == id do
			Map.delete(state, key)
		else
			state
		end
	end

end